{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Guest House - Hard"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading spark-stubs, spark-hive\n",
      "Adding Hive conf dir /opt/hive/conf to classpath\n",
      "Creating SparkSession\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "SLF4J: No SLF4J providers were found.\n",
      "SLF4J: Defaulting to no-operation (NOP) logger implementation\n",
      "SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<a target=\"_blank\" href=\"http://jupyter:4040\">Spark UI</a>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\u001b[32mimport \u001b[39m\u001b[36m$ivy.$                                  \n",
       "\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36morg.apache.log4j.{Level, Logger}\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36morg.apache.spark._\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36morg.apache.spark.sql._\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36morg.apache.spark.sql.types._\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36morg.apache.spark.sql.functions._\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36morg.apache.spark.sql.expressions.Window\n",
       "\n",
       "\u001b[39m\n",
       "\u001b[36mspark\u001b[39m: \u001b[32mSparkSession\u001b[39m = org.apache.spark.sql.SparkSession@460d05be"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import $ivy.`org.apache.spark::spark-sql:3.4.0`\n",
    "\n",
    "import org.apache.log4j.{Level, Logger}\n",
    "Logger.getLogger(\"org\").setLevel(Level.OFF)\n",
    "\n",
    "import org.apache.spark._\n",
    "import org.apache.spark.sql._\n",
    "import org.apache.spark.sql.types._\n",
    "import org.apache.spark.sql.functions._\n",
    "import org.apache.spark.sql.expressions.Window\n",
    "\n",
    "val spark = {\n",
    "    NotebookSparkSession.builder()\n",
    "    .progress(false)\n",
    "    .appName(\"app13-3\")\n",
    "    // .master(\"spark://192.168.31.31:7077\")\n",
    "    .master(\"local[*]\")\n",
    "    .config(\"spark.sql.warehouse.dir\", \n",
    "            \"hdfs://192.168.31.31:9000/user/hive/warehouse\") \n",
    "    .config(\"spark.cores.max\", \"4\") \n",
    "    .config(\"spark.executor.instances\", \"1\") \n",
    "    .config(\"spark.executor.cores\", \"2\") \n",
    "    .config(\"spark.executor.memory\", \"10g\") \n",
    "    .config(\"spark.shuffle.service.enabled\", \"false\") \n",
    "    .config(\"spark.dynamicAllocation.enabled\", \"false\") \n",
    "    .config(\"spark.sql.catalogImplementation\", \"hive\")\n",
    "    .config(\"spark.sql.repl.eagerEval.enabled\", \"true\")\n",
    "    .config(\"spark.driver.allowMultipleContexts\", \"true\")\n",
    "    .getOrCreate()\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[32mimport \u001b[39m\u001b[36mspark.implicits._\n",
       "\u001b[39m\n",
       "defined \u001b[32mfunction\u001b[39m \u001b[36msc\u001b[39m\n",
       "\u001b[36mhiveCxt\u001b[39m: \u001b[32msql\u001b[39m.\u001b[32mhive\u001b[39m.\u001b[32mHiveContext\u001b[39m = org.apache.spark.sql.hive.HiveContext@7cb484c4"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import spark.implicits._\n",
    "def sc = spark.sparkContext\n",
    "val hiveCxt = new org.apache.spark.sql.hive.HiveContext(sc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "defined \u001b[32mclass\u001b[39m \u001b[36mRichDF\u001b[39m"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// Credit to Aivean\n",
    "implicit class RichDF(val ds:DataFrame) {\n",
    "    def showHTML(limit: Int = 50, truncate: Int = 100) = {\n",
    "        import xml.Utility.escape\n",
    "        val data = ds.take(limit)\n",
    "        val header = ds.schema.fieldNames.toSeq        \n",
    "        val rows: Seq[Seq[String]] = data.map { row =>\n",
    "          row.toSeq.map {cell =>\n",
    "            val str = cell match {\n",
    "              case null => \"null\"\n",
    "              case binary: Array[Byte] => binary.map(\"%02X\".format(_)).mkString(\"[\", \" \", \"]\")\n",
    "              case array: Array[_] => array.mkString(\"[\", \", \", \"]\")\n",
    "              case seq: Seq[_] => seq.mkString(\"[\", \", \", \"]\")\n",
    "              case _ => cell.toString\n",
    "            }\n",
    "            if (truncate > 0 && str.length > truncate) {\n",
    "              // do not show ellipses for strings shorter than 4 characters.\n",
    "              if (truncate < 4) str.substring(0, truncate)\n",
    "              else str.substring(0, truncate - 3) + \"...\"\n",
    "            } else {\n",
    "              str\n",
    "            }\n",
    "          }: Seq[String]\n",
    "        }\n",
    "    publish.html(s\"\"\" <table>\n",
    "                <tr>\n",
    "                 ${header.map(h => s\"<th>${escape(h)}</th>\").mkString}\n",
    "                </tr>\n",
    "                ${rows.map {row =>\n",
    "                  s\"<tr>${row.map{c => s\"<td>${escape(c)}</td>\" }.mkString}</tr>\"\n",
    "                }.mkString}\n",
    "            </table>\n",
    "        \"\"\")\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[36mbooking\u001b[39m: \u001b[32mDataFrame\u001b[39m = [booking_id: int, booking_date: string ... 6 more fields]\n",
       "\u001b[36mguest\u001b[39m: \u001b[32mDataFrame\u001b[39m = [id: int, first_name: string ... 2 more fields]\n",
       "\u001b[36mroom\u001b[39m: \u001b[32mDataFrame\u001b[39m = [id: int, room_type: string ... 1 more field]\n",
       "\u001b[36mrate\u001b[39m: \u001b[32mDataFrame\u001b[39m = [room_type: string, occupancy: int ... 1 more field]\n",
       "\u001b[36mextra\u001b[39m: \u001b[32mDataFrame\u001b[39m = [extra_id: int, booking_id: int ... 2 more fields]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "val booking = hiveCxt.table(\"sqlzoo.booking\")\n",
    "val guest = hiveCxt.table(\"sqlzoo.guest\")\n",
    "val room = hiveCxt.table(\"sqlzoo.room\")\n",
    "val rate = hiveCxt.table(\"sqlzoo.rate\")\n",
    "val extra = hiveCxt.table(\"sqlzoo.extra\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 11.\n",
    "Coincidence. Have two guests with the same surname ever stayed in the hotel on the evening? Show the last name and both first names. Do not include duplicates.\n",
    "\n",
    "```\n",
    "+-----------+------------+-------------+\n",
    "| last_name | first_name | first_name  |\n",
    "+-----------+------------+-------------+\n",
    "| Davies    | Philip     | David T. C. |\n",
    "| Evans     | Graham     | Mr Nigel    |\n",
    "| Howarth   | Mr George  | Sir Gerald  |\n",
    "| Jones     | Susan Elan | Mr Marcus   |\n",
    "| Lewis     | Clive      | Dr Julian   |\n",
    "| McDonnell | John       | Dr Alasdair |\n",
    "+-----------+------------+-------------+\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       " <table>\n",
       "                <tr>\n",
       "                 <th>last_name</th><th>first_name1</th><th>first_name</th>\n",
       "                </tr>\n",
       "                <tr><td>Davies</td><td>Philip</td><td>David T. C.</td></tr><tr><td>Evans</td><td>Graham</td><td>Mr Nigel</td></tr><tr><td>Howarth</td><td>Mr George</td><td>Sir Gerald</td></tr><tr><td>Jones</td><td>Susan Elan</td><td>Mr Marcus</td></tr><tr><td>Lewis</td><td>Clive</td><td>Dr Julian</td></tr><tr><td>McDonnell</td><td>John</td><td>Dr Alasdair</td></tr>\n",
       "            </table>\n",
       "        "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\u001b[36mt\u001b[39m: \u001b[32mDataFrame\u001b[39m = [last_name: string, first_name: string ... 4 more fields]\n",
       "\u001b[36ma\u001b[39m: \u001b[32mDataset\u001b[39m[\u001b[32mRow\u001b[39m] = [last_name: string, first_name: string ... 9 more fields]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "val t = (guest.join(booking, (guest(\"id\")===booking(\"guest_id\")))\n",
    "     .select(\"last_name\", \"first_name\", \"booking_date\", \"nights\", \"id\")\n",
    "     .withColumn(\"checkout\", date_add(col(\"booking_date\"), col(\"nights\")-lit(1))))\n",
    "\n",
    "val a = (t.join(t.alias(\"t1\"), \"last_name\", joinType=\"full\")\n",
    "     .toDF(\"last_name\", \"first_name\", \"booking_date\", \"nights\", \"id\", \"checkout\",\n",
    "           \"first_name1\", \"booking_date1\", \"nights1\", \"id1\", \"checkout1\")\n",
    "     .filter(col(\"first_name\") !== col(\"first_name1\")))\n",
    "\n",
    "(a.filter((((col(\"booking_date\") >= col(\"booking_date1\")) && \n",
    "            (col(\"booking_date\") <= col(\"checkout1\"))) ||\n",
    "          ((col(\"booking_date1\") >= col(\"booking_date\")) && \n",
    "            (col(\"booking_date1\") <= col(\"checkout\")))) &&\n",
    "          (col(\"id\") >= col(\"id1\")))\n",
    " .select(\"last_name\", \"first_name1\", \"first_name\")\n",
    " .distinct()\n",
    " .orderBy(\"last_name\")\n",
    " .showHTML())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 12.\n",
    "Check out per floor. The first digit of the room number indicates the floor – e.g. room 201 is on the 2nd floor. For each day of the week beginning 2016-11-14 show how many rooms are being vacated that day by floor number. Show all days in the correct order.\n",
    "\n",
    "```\n",
    "+------------+-----+-----+-----+\n",
    "| i          | 1st | 2nd | 3rd |\n",
    "+------------+-----+-----+-----+\n",
    "| 2016-11-14 |   5 |   3 |   4 |\n",
    "| 2016-11-15 |   6 |   4 |   1 |\n",
    "| 2016-11-16 |   2 |   2 |   4 |\n",
    "| 2016-11-17 |   5 |   3 |   6 |\n",
    "| 2016-11-18 |   2 |   3 |   2 |\n",
    "| 2016-11-19 |   5 |   5 |   1 |\n",
    "| 2016-11-20 |   2 |   2 |   2 |\n",
    "+------------+-----+-----+-----+\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       " <table>\n",
       "                <tr>\n",
       "                 <th>checkout</th><th>1st</th><th>2nd</th><th>3rd</th>\n",
       "                </tr>\n",
       "                <tr><td>2016-11-14</td><td>5</td><td>3</td><td>4</td></tr><tr><td>2016-11-15</td><td>6</td><td>4</td><td>1</td></tr><tr><td>2016-11-16</td><td>2</td><td>2</td><td>4</td></tr><tr><td>2016-11-17</td><td>5</td><td>3</td><td>6</td></tr><tr><td>2016-11-18</td><td>2</td><td>3</td><td>2</td></tr><tr><td>2016-11-19</td><td>5</td><td>5</td><td>1</td></tr><tr><td>2016-11-20</td><td>2</td><td>2</td><td>2</td></tr>\n",
       "            </table>\n",
       "        "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "(booking\n",
    " .withColumn(\"checkout\", date_add(col(\"booking_date\"), col(\"nights\")))\n",
    " .withColumn(\"floor\", substring(col(\"room_no\"), 1, 1))\n",
    " .filter(col(\"checkout\").between(\"2016-11-14\", \"2016-11-20\"))\n",
    " .na.replace(\"floor\", Map(\"1\" -> \"1st\", \"2\" -> \"2nd\", \"3\" -> \"3rd\", \"4\" -> \"4th\"))\n",
    " .groupBy(\"checkout\")\n",
    " .pivot(\"floor\")\n",
    " .count()\n",
    " .orderBy(\"checkout\")\n",
    " .showHTML())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 13.\n",
    "Free rooms? List the rooms that are free on the day 25th Nov 2016.\n",
    "\n",
    "```\n",
    "+-----+\n",
    "| id  |\n",
    "+-----+\n",
    "| 207 |\n",
    "| 210 |\n",
    "| 304 |\n",
    "+-----+\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       " <table>\n",
       "                <tr>\n",
       "                 <th>id</th>\n",
       "                </tr>\n",
       "                <tr><td>207</td></tr><tr><td>210</td></tr><tr><td>304</td></tr>\n",
       "            </table>\n",
       "        "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "(room.join(\n",
    "    booking\n",
    "    .withColumn(\"checkout\", date_add(col(\"booking_date\"), col(\"nights\")-1))\n",
    "    .filter((col(\"booking_date\") <= \"2016-11-25\") && \n",
    "            (col(\"checkout\") >= \"2016-11-25\")),\n",
    "    (booking(\"room_no\")===room(\"id\")), joinType=\"left_anti\")\n",
    " .select(\"id\")\n",
    " .showHTML())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 14.\n",
    "Single room for three nights required. A customer wants a single room for three consecutive nights. Find the first available date in December 2016.\n",
    "\n",
    "```\n",
    "+-----+------------+\n",
    "| id  | MIN(i)     |\n",
    "+-----+------------+\n",
    "| 201 | 2016-12-11 |\n",
    "+-----+------------+\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       " <table>\n",
       "                <tr>\n",
       "                 <th>room_no</th><th>checkout</th>\n",
       "                </tr>\n",
       "                <tr><td>201</td><td>2016-12-11</td></tr>\n",
       "            </table>\n",
       "        "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "(booking\n",
    " .withColumn(\"checkout\", date_add(col(\"booking_date\"), col(\"nights\")))\n",
    " .join(room.filter(col(\"room_type\")===\"single\"), \n",
    "       (booking(\"room_no\")===room(\"id\")), joinType=\"right\")\n",
    " .filter(((col(\"checkout\") >= \"2016-12-01\") && \n",
    "          (col(\"booking_date\") <= \"2016-12-31\")) || \n",
    "         (col(\"checkout\").isNull))\n",
    " .withColumn(\"next_booking\", lag(\"booking_date\", 1, \"2017-01-01\").over(\n",
    "     Window.partitionBy(\"room_no\").orderBy(\"room_no\")))\n",
    " .withColumn(\"diff\", (col(\"next_booking\")-col(\"checkout\")).cast(\"integer\"))\n",
    " .filter(col(\"diff\") >= 3)\n",
    " .select(\"room_no\", \"checkout\")\n",
    " .orderBy(\"checkout\")\n",
    " .limit(1)\n",
    " .showHTML())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 15.\n",
    "Gross income by week. Money is collected from guests when they leave. For each Thursday in November and December 2016, show the total amount of money collected from the previous Friday to that day, inclusive.\n",
    "\n",
    "```\n",
    "+------------+---------------+\n",
    "| Thursday   | weekly_income |\n",
    "+------------+---------------+\n",
    "| 2016-11-03 |          0.00 |\n",
    "| 2016-11-10 |      12608.94 |\n",
    "| 2016-11-17 |      13552.56 |\n",
    "| 2016-11-24 |      12929.69 |\n",
    "| 2016-12-01 |      11685.14 |\n",
    "| 2016-12-08 |      13093.79 |\n",
    "| 2016-12-15 |       8975.87 |\n",
    "| 2016-12-22 |       1395.77 |\n",
    "| 2016-12-29 |          0.00 |\n",
    "| 2017-01-05 |          0.00 |\n",
    "+------------+---------------+\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+----------+\n",
      "|  Thursday|\n",
      "+----------+\n",
      "|2016-11-03|\n",
      "|2016-11-10|\n",
      "|2016-11-17|\n",
      "|2016-11-24|\n",
      "|2016-12-01|\n",
      "|2016-12-08|\n",
      "|2016-12-15|\n",
      "|2016-12-22|\n",
      "|2016-12-29|\n",
      "+----------+\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\u001b[32mimport \u001b[39m\u001b[36mjava.time.LocalDate\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36mjava.time.DayOfWeek\n",
       "\u001b[39m\n",
       "\u001b[32mimport \u001b[39m\u001b[36mjava.time.temporal.TemporalAdjusters\n",
       "\n",
       "\u001b[39m\n",
       "defined \u001b[32mfunction\u001b[39m \u001b[36mgetThursdays\u001b[39m\n",
       "\u001b[36mthursdays\u001b[39m: \u001b[32mDataFrame\u001b[39m = [Thursday: date]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "// generate Thursdays\n",
    "import java.time.LocalDate\n",
    "import java.time.DayOfWeek\n",
    "import java.time.temporal.TemporalAdjusters\n",
    "\n",
    "def getThursdays(startDate: LocalDate, endDate: LocalDate): List[LocalDate] = {\n",
    "  val thursdays = scala.collection.mutable.ListBuffer.empty[LocalDate]\n",
    "  var nextThursday = startDate.`with`(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY))\n",
    "\n",
    "  while (!nextThursday.isAfter(endDate)) {\n",
    "    thursdays += nextThursday\n",
    "    nextThursday = nextThursday.plusWeeks(1)\n",
    "  }\n",
    "\n",
    "  thursdays.toList\n",
    "}\n",
    "\n",
    "val thursdays = spark.createDataset(\n",
    "    getThursdays(LocalDate.of(2016, 11, 1), LocalDate.of(2016, 12, 31))).toDF(\"Thursday\")\n",
    "\n",
    "thursdays.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       " <table>\n",
       "                <tr>\n",
       "                 <th>Thursday</th><th>weekly_income</th>\n",
       "                </tr>\n",
       "                <tr><td>2016-11-03</td><td>0.0</td></tr><tr><td>2016-11-10</td><td>12608.94</td></tr><tr><td>2016-11-17</td><td>13552.56</td></tr><tr><td>2016-11-24</td><td>12929.69</td></tr><tr><td>2016-12-01</td><td>11685.14</td></tr><tr><td>2016-12-08</td><td>13093.79</td></tr><tr><td>2016-12-15</td><td>8975.87</td></tr><tr><td>2016-12-22</td><td>1395.77</td></tr><tr><td>2016-12-29</td><td>0.0</td></tr>\n",
       "            </table>\n",
       "        "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "(booking\n",
    " .join(rate, (booking(\"occupants\")===rate(\"occupancy\")) && \n",
    "             (booking(\"room_type_requested\")===rate(\"room_type\")), \n",
    "       joinType=\"left\")\n",
    " .withColumn(\"income\", col(\"amount\") * col(\"nights\"))\n",
    " .select(\"booking_date\", \"nights\", \"income\")\n",
    " .union(booking\n",
    "        .join(extra, \"booking_id\", joinType=\"left\")\n",
    "        .withColumn(\"income\", col(\"amount\"))\n",
    "        .select(\"booking_date\", \"nights\", \"income\"))\n",
    " .withColumn(\"checkout\", date_add(col(\"booking_date\"), col(\"nights\")))\n",
    " .withColumn(\"wkd\", dayofweek(col(\"checkout\")))\n",
    " .withColumn(\"Thursday\", when(\n",
    "     col(\"wkd\")<=5, date_add(col(\"checkout\"), lit(5)-col(\"wkd\")))\n",
    "     .otherwise(date_add(col(\"checkout\"), lit(12)-col(\"wkd\"))))\n",
    " .join(thursdays, \"Thursday\", joinType=\"right\")\n",
    " .groupBy(\"Thursday\")\n",
    " .agg(round(sum(\"income\"), 2).alias(\"weekly_income\"))\n",
    " .na.fill(0, Array(\"weekly_income\"))\n",
    " .orderBy(\"Thursday\")\n",
    " .showHTML())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "spark.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Scala",
   "language": "scala",
   "name": "scala"
  },
  "language_info": {
   "codemirror_mode": "text/x-scala",
   "file_extension": ".sc",
   "mimetype": "text/x-scala",
   "name": "scala",
   "nbconvert_exporter": "script",
   "version": "2.12.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
